package org.raymond.icloud.common.starter.webFilter;

import java.io.IOException;
import java.util.*;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.baomidou.mybatisplus.core.toolkit.Sequence;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.raymond.icloud.common.core.bean.LogBean;
import org.raymond.icloud.common.core.constants.MQConstants;
import org.raymond.icloud.common.core.util.ApplicationContextUtils;
import org.raymond.icloud.common.core.util.FilterUtil;
import org.raymond.icloud.common.core.util.RequestUtil;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Slf4j
public class LogFilter extends AbstractFilter {
    private Sequence sequence;
    private RabbitTemplate rabbitTemplate;
    private ThreadPoolTaskExecutor asyncLogExecutor;

    protected void initFilterBean() throws ServletException {
        ApplicationContext ctx = ApplicationContextUtils.getApplicationContext();
        sequence = ctx.getBean("sequence", Sequence.class);
        rabbitTemplate = ctx.getBean("rabbitTemplate", RabbitTemplate.class);
        asyncLogExecutor = ctx.getBean("asyncLogExecutor", ThreadPoolTaskExecutor.class);
    }

    @Override
    protected void doFilterSub(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        Date startTime = new Date();
        Exception t = null;
        try{
            filterChain.doFilter(request, response);
        }catch(Exception e){
            t = e;
            throw e;
        }finally {
            saveLog(request, response, startTime, new Date(), t);
        }
    }

    private void saveLog(final HttpServletRequest request, final HttpServletResponse response,
                       final Date startTime, final Date endTime, final Throwable t){
        if (isAsyncStarted(request)) {
            FilterUtil.copyResponse(response);
            return;
        }

        try {
            final String requestUri = request.getRequestURI();
            final String requestHeaders = FilterUtil.getRequestHeaders(request);
            final String headersForLog = FilterUtil.getHeadersForLog(request);
            final String responseHeaders = FilterUtil.getResponseHeaders(response);
            final String requestIp = FilterUtil.getRequestIp(request);
            final String requestParams = FilterUtil.getRequestParams(request);
            final String method = request.getMethod();
            final String requestString = FilterUtil.isUpload(request) ? StringUtils.EMPTY : FilterUtil.getRequestString(request);
            final String responseString = FilterUtil.isDownload(response) ? StringUtils.EMPTY : FilterUtil.getResponseString(response);
            final int responseStatus = response.getStatus();
            final long id = sequence.nextId();

            final LogBean logBean = LogBean.builder()
                    .id(id)
                    .requestIP(requestIp).requestURI(requestUri)
                    .method(method).clientTime(new Date())
                    .headers(headersForLog).params(requestParams)
                    .request(requestString).response(responseString)
                    .status(responseStatus)
                    .startTime(startTime).endTime(endTime)
                    .build();

            asyncLogExecutor.execute(()->{
                log.info("requestHeaders:{}", requestHeaders);
                log.info("responseHeaders:{}", responseHeaders);
                doSaveLog(logBean);
            });
        } catch (Throwable e) {
            log.error("saveLog error {}", e.getMessage());
        } finally {
            FilterUtil.copyResponse(response);
        }
    }

    public void doSaveLog(LogBean logBean){
        rabbitTemplate.convertAndSend(MQConstants.EXCHANGE_LOG_COMMON, MQConstants.ROUTING_LOG, logBean);
    }

    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        String contextPath = request.getContextPath();
        String originRequestUri = request.getRequestURI();
        final String requestUri = originRequestUri.substring(contextPath.length());

        return super.shouldNotFilter(request) || RequestUtil.noLog(requestUri);
    }
}
